package wlsAgent;

import java.io.IOException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.naming.Context;

import org.apache.log4j.Logger;
import org.dom4j.Document;

import wlsAgent.xml.XmlWorker;

public class FusionStateCollector {

	// initialize necessary credentialss
	private String hostname;

	private String portString;

	private String username;

	private String password;

	private MBeanServerConnection connection;

	private JMXConnector connector;

	private static final ObjectName domainRuntimeServiceMBeanObjName;

	private Document coreDoc;

	private XmlWorker xmlw;

	private Map pinnedInfoMap;

	private ObjectName[] serverRT;

	private static Logger log;

	static {

		// Initializing the object name for DomainRuntimeServiceMBean
		// so it can be used throughout the class.
		try {
			log = Logger.getLogger(wlsAgent.FusionStateCollector.class);
			domainRuntimeServiceMBeanObjName = new ObjectName(
					"com.bea:Name=DomainRuntimeService,Type=weblogic.management.mbeanservers.domainruntime.DomainRuntimeServiceMBean");
		} catch (MalformedObjectNameException e) {
			throw new AssertionError(e.getMessage());
		}
	}

	/**
	 */
	public FusionStateCollector() {
		super();
		xmlw = new XmlWorker();
		pinnedInfoMap = new HashMap(20);
	}

	public void setCoreFields(Document doc) {
		this.coreDoc = doc;
		String attrNameStrs[] = { "adminAddress", "adminPort", "principal",
				"password" };
		String[] coreFiledsStr = xmlw.getAttrValuesOfSingleNode(this.coreDoc,
				"//Domain", attrNameStrs);
		this.hostname = coreFiledsStr[0];
		this.portString = coreFiledsStr[1];
		this.username = coreFiledsStr[2];
		this.password = coreFiledsStr[3];

	}

	public Document getCoreDoc() {
		return this.coreDoc;
	}

	/*
	 * Initialize connection to the Domain Runtime MBean Server
	 */
	public boolean initConnection() {
		try {
			String protocol = "t3";
			Integer portInteger = Integer.valueOf(portString);
			int port = portInteger.intValue();
			String jndiroot = "/jndi/";
			String domainruntime = "weblogic.management.mbeanservers.domainruntime";
			JMXServiceURL serviceURL = new JMXServiceURL(protocol, hostname,
					port, jndiroot + domainruntime);
			Hashtable h = new Hashtable();
			h.put(Context.SECURITY_PRINCIPAL, username);
			h.put(Context.SECURITY_CREDENTIALS, password);
			h.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES,
					"weblogic.management.remote");
			h.put("jmx.remote.x.request.waiting.timeout", new Long(10000));

			connector = JMXConnectorFactory.connect(serviceURL, h);
			connection = connector.getMBeanServerConnection();
			serverRT = getServerRuntimes();
			log.info("Got " + serverRT.length + " wls instances...\n");
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			log.error("Cannot initialize connection due to: ", e);
			return false;
		}
	}

	/*
	 * Print an array of ServerRuntimeMBeans. This MBean is the root of the
	 * runtime MBean hierarchy, and each server in the domain hosts its own
	 * instance.
	 */
	public ObjectName[] getServerRuntimes() {
		try {
			return (ObjectName[]) connection.getAttribute(
					domainRuntimeServiceMBeanObjName, "ServerRuntimes");
		} catch (Exception e) {
			log.error("Cannot get ServerRuntimeMBeans: ", e);
			return null;
		}
	}

	public ObjectName getServerRuntimesBySvrName(String sName) {
		try {
			int length = (int) serverRT.length;
			for (int i = 0; i < length; i++) {
				if (sName.equals((String) connection.getAttribute(serverRT[i],
						"Name"))) {
					return serverRT[i];
				}
			}
			return null;
		} catch (Exception e) {
			log.error("Cannot get ServerRuntimeMBean by given svrname: ", e);
			return null;
		}
	}

	/*
	 * Get Mbean object by name and parent MBean
	 */
	public ObjectName getRuntimeMbean(ObjectName parentMbeanObj,
			String sonMbeanObj) {
		try {
			if (connection != null) {
				if (sonMbeanObj.equals("ServerRuntime")) {
					return parentMbeanObj;
				}
				ObjectName rmb = (ObjectName) connection.getAttribute(
						parentMbeanObj, sonMbeanObj);
				return rmb;
			} else {
				return null;
			}
		} catch (Exception e) {
			log
					.error(
							"Cannot get RuntimeMBean by given parentMbeanObj and sonMbeanObj: ",
							e);
			return null;
		}
	}

	/*
	 * Iterate through given Mbean and get wanted attribute values
	 */
	public Map getMBeanInfoMap(Vector<String> attrVec, String mBeanType,
			ObjectName svrRt) {
		try {
			if (attrVec != null) {
				Map mbeanInfoMap = new HashMap(attrVec.capacity());
				ObjectName currRtmMbea = this.getRuntimeMbean(svrRt, mBeanType);

				for (Iterator i = attrVec.iterator(); i.hasNext();) {
					String curAttr = (String) i.next();
					Object curAttrVal = connection.getAttribute(currRtmMbea,
							curAttr);
					if (curAttrVal == null) {
						mbeanInfoMap.put(curAttr, "n/a");
					} else {
						mbeanInfoMap.put(curAttr, connection.getAttribute(
								currRtmMbea, curAttr));
					}
				}

				return mbeanInfoMap;
			} else {
				return null;
			}
		} catch (Exception e) {
			log
					.error(
							"Cannot get MBeanInfoMap by given MBean and attributes vector: ",
							e);
			return null;
		}
	}

	public void closeCollector() {
		try {
			connector.close();
		} catch (IOException ioe) {
			log.error("Cannot close connector: ", ioe);
		}
	}

	public Document genFeedData(boolean isInitial) {
		try {
			if (isInitial) {
				ObjectName domCfgMbean = this.getDomainConfigurationMbean();

				pinnedInfoMap.put("DomainVersion", connection.getAttribute(
						domCfgMbean, "DomainVersion"));
				pinnedInfoMap.put("GuardianEnabled", connection.getAttribute(
						domCfgMbean, "GuardianEnabled"));
				pinnedInfoMap.put("AdminServerName", connection.getAttribute(
						domCfgMbean, "AdminServerName"));
				pinnedInfoMap.put("Name", connection.getAttribute(domCfgMbean,
						"Name"));
				pinnedInfoMap.put("ProductionModeEnabled", connection
						.getAttribute(domCfgMbean, "ProductionModeEnabled"));
				pinnedInfoMap.put("RootDirectory", connection.getAttribute(
						domCfgMbean, "RootDirectory"));

				ObjectName adminSvrRtmObjName = getServerRuntimesBySvrName((String) connection
						.getAttribute(domCfgMbean, "AdminServerName"));

				ObjectName jvmRtmObjName = (ObjectName) connection
						.getAttribute(adminSvrRtmObjName, "JVMRuntime");

				pinnedInfoMap.put("OSName", connection.getAttribute(
						jvmRtmObjName, "OSName"));
				pinnedInfoMap.put("OSVersion", connection.getAttribute(
						jvmRtmObjName, "OSVersion"));
				pinnedInfoMap.put("JavaVendor", connection.getAttribute(
						jvmRtmObjName, "JavaVendor"));
				pinnedInfoMap.put("JavaVersion", connection.getAttribute(
						jvmRtmObjName, "JavaVersion"));
				pinnedInfoMap.put("ActivationTime", connection.getAttribute(
						adminSvrRtmObjName, "ActivationTime"));

				pinnedInfoMap.put("ServerNum", String
						.valueOf(this.serverRT.length));

				return this.xmlw.genIniXml(pinnedInfoMap);

			} else {
				return this.xmlw.genMonXml(this.getCoreDoc(), this, this
						.getServerRT());
			}
		} catch (Exception e) {
			log.error("IsInitial: " + isInitial
					+ ". Cannot generate MBeanInfoMap...", e);
			return null;
		}
	}

	public ObjectName getDomainConfigurationMbean() {
		try {
			return (ObjectName) this.connection.getAttribute(
					this.domainRuntimeServiceMBeanObjName,
					"DomainConfiguration");
		} catch (Exception e) {
			log.error("Cannot get DomainConfigurationMbean: ", e);
			return null;
		}
	}

	public ObjectName[] getServerRT() {
		return this.serverRT;
	}

	public static void main(String[] args) throws Exception {
	}
}